package net.qhzw.vip.qiyu.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import io.minio.*;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import net.qhzw.vip.qiyu.handler.MyException;
import net.qhzw.vip.qiyu.model.BucketPolicyConfigDto;
import net.qhzw.vip.qiyu.model.MinioUploadDto;
import net.qhzw.vip.qiyu.result.ResultCode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Component
@Api(tags = "文件上传")
@Slf4j
public class FileTemplate {

    @Value("${minio.endpoint}")
    private String ENDPOINT;

    @Value("${minio.bucketName}")
    private String BUCKET_NAME;

    @Value("${minio.accessKey}")
    private String ACCESS_KEY;

    @Value("${minio.secretKey}")
    private String SECRET_KEY;


    /***
     * 文件上传
     * @param file
     * @return
     */
    public String fileUpload(MultipartFile file) {
        log.info("文件上传,文件名{}, 文件大小{}", file.getOriginalFilename(), file.getSize());
        try {
            MinioClient minioClient = initMinioClient();
            String filename = file.getOriginalFilename();
            // 设置存储对象名称
            String objectName = DateUtil.today().replaceAll("-", "/") + "/" + filename;
            // 使用putObject上传一个文件到存储桶中
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(BUCKET_NAME)
                    .object(objectName)
                    .contentType(file.getContentType())
                    .stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build();
            minioClient.putObject(putObjectArgs);
            log.info("文件上传成功 {}", JSON.toJSONString(putObjectArgs, true));
            MinioUploadDto minioUploadDto = new MinioUploadDto();
            minioUploadDto.setName(filename);
            minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
            return BUCKET_NAME + "/" + objectName;
        } catch (Exception e) {
            log.info("【上传发生错误: {}！】", e.getMessage());
            throw new MyException(ResultCode.YUNAVAILABLE);
        }
    }

    /***
     * 多文件上传
     * @param files
     * @return
     */
    public List<String> fileUploads(MultipartFile[] files) {
        List<String> result = new ArrayList<>();
        try {
            for (MultipartFile file : files) {
                log.info("文件上传,文件名{}, 文件大小{}", file.getOriginalFilename(), file.getSize());
                String filename = file.getOriginalFilename();
                // 设置存储对象名称
                String objectName = DateUtil.today().replaceAll("-", "/") + "/" + filename;
                // 使用putObject上传一个文件到存储桶中
                PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                        .bucket(BUCKET_NAME)
                        .object(objectName)
                        .contentType(file.getContentType())
                        .stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build();
                MinioClient minioClient = initMinioClient();
                minioClient.putObject(putObjectArgs);
                log.info("文件上传成功 {}", JSON.toJSONString(putObjectArgs, true));
                MinioUploadDto minioUploadDto = new MinioUploadDto();
                minioUploadDto.setName(filename);
                minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
                result.add(BUCKET_NAME + "/" + objectName);
            }
            return result;
        } catch (Exception e) {
            log.info("【上传发生错误: {}！】", e.getMessage());
            throw new MyException(e.getMessage());
        }
    }


    /***
     * 文件删除
     * @param objectName
     * @return
     */
    public boolean delete(String objectName) {
        try {
            MinioClient minioClient = MinioClient.builder()
                    .endpoint(ENDPOINT)
                    .credentials(ACCESS_KEY, SECRET_KEY)
                    .build();
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(BUCKET_NAME).object(objectName).build());
            return true;
        } catch (Exception e) {
            log.error("文件删除失败, message: {}", JSON.toJSONString(e.getMessage(), true));
        }
        return false;
    }

    private MinioClient initMinioClient() {
        try {
            //创建一个MinIO的Java客户端
            MinioClient minioClient = MinioClient.builder()
                    .endpoint(ENDPOINT)
                    .credentials(ACCESS_KEY, SECRET_KEY)
                    .build();
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET_NAME).build());
            if (isExist) {
                log.info("存储桶已经存在！");
            } else {
                //创建存储桶并设置只读权限
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build());
                BucketPolicyConfigDto bucketPolicyConfigDto = createBucketPolicyConfigDto(BUCKET_NAME);
                SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
                        .bucket(BUCKET_NAME)
                        .config(JSONUtil.toJsonStr(bucketPolicyConfigDto))
                        .build();
                minioClient.setBucketPolicy(setBucketPolicyArgs);
            }
            return minioClient;
        } catch (Exception e) {
            log.info("【上传发生错误: {}！】", e.getMessage());
            throw new MyException(e.getMessage());
        }

    }

    private BucketPolicyConfigDto createBucketPolicyConfigDto(String bucketName) {
        BucketPolicyConfigDto.Statement statement = BucketPolicyConfigDto.Statement.builder()
                .Effect("Allow")
                .Principal("*")
                .Action("s3:GetObject")
                .Resource("arn:aws:s3:::" + bucketName + "/*.**").build();
        return BucketPolicyConfigDto.builder()
                .Version("2023-01-01")
                .Statement(CollUtil.toList(statement))
                .build();
    }

    public String fileUpload1(MultipartFile file ,String name) {
        log.info("文件上传,文件名{}, 文件大小{}", file.getOriginalFilename(), file.getSize());
        try {
            MinioClient minioClient = initMinioClient();
            String filename = file.getOriginalFilename();
            // 设置存储对象名称
            String objectName = "repliar" +"/"+ DateUtil.today().replaceAll("-", "/") + "/" + name +"/"+  filename;
            //获取水印照片
            //MultipartFile multipartFile = ImageUtil.addWorkMarkToMutipartFile(file, name);
            // 使用putObject上传一个文件到存储桶中
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(BUCKET_NAME)
                    .object(objectName)
                    .contentType(file.getContentType())
                    .stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build();
            minioClient.putObject(putObjectArgs);
            log.info("文件上传成功 {}", JSON.toJSONString(putObjectArgs, true));
            MinioUploadDto minioUploadDto = new MinioUploadDto();
            minioUploadDto.setName(filename);
            minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
            //return BUCKET_NAME + "/" + objectName;
            return  objectName;
        } catch (Exception e) {
            log.info("【上传发生错误: {}！】", e.getMessage());
            throw new MyException(ResultCode.YUNAVAILABLE);
        }
    }





    public void batchDownload(List<String> filenames, String zipName, HttpServletResponse res, HttpServletRequest req){
        try {
            BucketExistsArgs bucketArgs = BucketExistsArgs.builder().bucket(BUCKET_NAME).build();
            MinioClient minioClient = initMinioClient();
            boolean bucketExists = minioClient.bucketExists(bucketArgs);
            BufferedOutputStream bos = null;
            res.reset();
            bos = new BufferedOutputStream(res.getOutputStream());
            ZipOutputStream out = new ZipOutputStream(bos);
            res.setHeader("Access-Control-Allow-Origin", "*");
            for (int i=0;i<filenames.size();i++) {
                GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(BUCKET_NAME)
                        .object(filenames.get(i)).build();
                InputStream object = minioClient.getObject(objectArgs);
                byte buf[] = new byte[1024];
                int length = 0;
                res.setCharacterEncoding("utf-8");
                res.setContentType("application/force-download");// 设置强制下载不打开
                res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
                res.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoding(zipName,req) + ".zip");
                out.putNextEntry(new ZipEntry(filenames.get(i)));
                while ((length = object.read(buf)) > 0) {
                    out.write(buf, 0, length);
                }
            }
            out.close();
            bos.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new MyException("照片存在异常");
        }
    }
    /**
     *  设置不同浏览器编码
     * @param filename 文件名称
     * @param request 请求对象
     */
    public static String filenameEncoding(String filename, HttpServletRequest request) throws UnsupportedEncodingException {
        // 获得请求头中的User-Agent
        String agent = request.getHeader("User-Agent");
        // 根据不同的客户端进行不同的编码

        if (agent.contains("MSIE")) {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        } else if (agent.contains("Firefox")) {
            // 火狐浏览器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }


}
